<template>
  <div class="min-h-screen bg-gradient-to-br from-blue-50 to-purple-50 py-8">
    <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
      <!-- Header -->
      <div class="text-center mb-8">
        <h1 class="text-3xl font-bold text-gray-900 mb-4">
          <span class="text-gradient">MD5 哈希生成器</span>
        </h1>
        <p class="text-lg text-gray-600">计算字符串或文件的 MD5 值，支持复制与下载</p>
      </div>

      <div class="bg-white rounded-xl shadow-lg p-6">
        <!-- Input Area -->
        <div class="mb-6">
          <label class="block text-sm font-medium text-gray-700 mb-2">输入文本</label>
          <textarea
            v-model="inputText"
            placeholder="请输入要计算MD5的文本..."
            class="textarea-field h-32"
            @input="computeHash"
          ></textarea>
          <div class="mt-2 text-sm text-gray-500">字符数: {{ inputText.length }}</div>
        </div>

        <!-- Output Area -->
        <div class="mb-6">
          <label class="block text-sm font-medium text-gray-700 mb-2">MD5 结果</label>
          <textarea
            v-model="outputText"
            readonly
            class="textarea-field h-20 bg-gray-50"
            placeholder="MD5 结果将显示在此处..."
          ></textarea>
          <div class="mt-2 flex justify-between items-center">
            <span class="text-sm text-gray-500">格式: {{ uppercase ? '大写' : '小写' }}</span>
            <button
              @click="copyToClipboard"
              class="secondary-button text-sm py-2 px-4"
              :disabled="!outputText"
            >
              {{ copied ? '已复制' : '复制结果' }}
            </button>
          </div>
        </div>

        <!-- Actions -->
        <div class="flex flex-wrap gap-3 mb-4">
          <button @click="computeHash" class="primary-button" :disabled="!inputText">计算MD5</button>
          <button @click="clearAll" class="secondary-button">清空内容</button>
          <button @click="loadSample" class="secondary-button">加载示例</button>
        </div>

        <!-- File Hashing -->
        <div class="mt-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
          <div class="flex items-center space-x-3">
            <button @click="triggerFileInput" class="secondary-button">选择文件计算 MD5</button>
            <input ref="fileInputRef" type="file" class="hidden" @change="handleFileChange" />
            <div v-if="fileInfo.name" class="text-sm text-gray-600">
              文件: {{ fileInfo.name }} • 大小: {{ fileInfo.size }} • MD5: {{ fileInfo.hash }}
            </div>
          </div>

          <div class="flex items-center space-x-3">
            <label class="inline-flex items-center space-x-2 text-sm text-gray-700">
              <input type="checkbox" v-model="uppercase" class="form-checkbox" />
              <span>大写</span>
            </label>
            <button @click="downloadResult" class="secondary-button" :disabled="!outputText">下载结果</button>
          </div>
        </div>

        <div v-if="errorMessage" class="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg">
          <p class="text-red-600 text-sm">{{ errorMessage }}</p>
        </div>
      </div>

      <!-- Instructions -->
      <div class="mt-8 bg-white rounded-xl shadow-lg p-6">
        <h3 class="text-lg font-semibold text-gray-900 mb-4">使用说明</h3>
        <ul class="space-y-2 text-gray-600">
          <li>• 输入文本或选择文件，然后点击“计算MD5”或等待自动计算</li>
          <li>• MD5 常用于校验文件完整性（注意：MD5 不适合用作密码学安全哈希）</li>
          <li>• 支持复制与下载哈希值</li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { useTools } from '@/composables/useTools'

const { copyToClipboard: toolsCopy, downloadFile } = useTools()

const inputText = ref('')
const outputText = ref('')
const copied = ref(false)
const uppercase = ref(false)
const errorMessage = ref('')
const fileInputRef = ref(null)
const fileInfo = ref({ name: '', size: '', hash: '' })

// 基于 Paul Johnston 的 MD5 实现（轻量复写，适用于字符串与字节数组）
// 该实现为常见公共域/示例实现，已适配用于本页面
function toHexLe(arr) {
  const hexChr = '0123456789abcdef'
  let str = ''
  for (let i = 0; i < arr.length; i++) {
    const b = arr[i]
    str += hexChr.charAt((b >> 4) & 0x0f) + hexChr.charAt(b & 0x0f)
  }
  return str
}

function md5ArrayBuffer(arrayBuffer) {
  // 使用简化的实现：先将 ArrayBuffer 转为字节数组（Uint8Array），再按 RFC 1321 算法实现
  const bytes = new Uint8Array(arrayBuffer)
  return md5Bytes(bytes)
}

// 简化的 md5Bytes 实现（小型）
function md5Bytes(bytes) {
  // Convert bytes to word array
  const len = bytes.length
  const words = []
  for (let i = 0; i < len; i += 4) {
    words[i >> 2] = (bytes[i] || 0) | ((bytes[i + 1] || 0) << 8) | ((bytes[i + 2] || 0) << 16) | ((bytes[i + 3] || 0) << 24)
  }

  // Append padding
  const bitLen = len * 8
  words[(len >> 2)] |= 0x80 << ((len % 4) * 8)
  words[((((len + 8) >> 6) + 1) * 16) - 2] = bitLen

  // Initialize variables
  let a = 0x67452301
  let b = 0xefcdab89
  let c = 0x98badcfe
  let d = 0x10325476

  const T = new Array(65)
  for (let i = 1; i <= 64; i++) T[i] = Math.floor(Math.abs(Math.sin(i)) * 0x100000000)

  function FF(a, b, c, d, x, s, t) { return ((a + ((b & c) | (~b & d)) + x + t) >>> 0).rotateLeft(s) + b }
  function GG(a, b, c, d, x, s, t) { return ((a + ((b & d) | (c & ~d)) + x + t) >>> 0).rotateLeft(s) + b }
  function HH(a, b, c, d, x, s, t) { return ((a + (b ^ c ^ d) + x + t) >>> 0).rotateLeft(s) + b }
  function II(a, b, c, d, x, s, t) { return ((a + (c ^ (b | ~d)) + x + t) >>> 0).rotateLeft(s) + b }

  // polyfill rotateLeft on Number prototype safely
  if (typeof Number.prototype.rotateLeft !== 'function') {
    Number.prototype.rotateLeft = function (n) { return ((this << n) | (this >>> (32 - n))) >>> 0 }
  }

  for (let i = 0; i < words.length; i += 16) {
    const olda = a, oldb = b, oldc = c, oldd = d

    // Round 1
    a = FF(a, b, c, d, words[i + 0], 7, T[1])
    d = FF(d, a, b, c, words[i + 1], 12, T[2])
    c = FF(c, d, a, b, words[i + 2], 17, T[3])
    b = FF(b, c, d, a, words[i + 3], 22, T[4])
    a = FF(a, b, c, d, words[i + 4], 7, T[5])
    d = FF(d, a, b, c, words[i + 5], 12, T[6])
    c = FF(c, d, a, b, words[i + 6], 17, T[7])
    b = FF(b, c, d, a, words[i + 7], 22, T[8])
    a = FF(a, b, c, d, words[i + 8], 7, T[9])
    d = FF(d, a, b, c, words[i + 9], 12, T[10])
    c = FF(c, d, a, b, words[i + 10], 17, T[11])
    b = FF(b, c, d, a, words[i + 11], 22, T[12])
    a = FF(a, b, c, d, words[i + 12], 7, T[13])
    d = FF(d, a, b, c, words[i + 13], 12, T[14])
    c = FF(c, d, a, b, words[i + 14], 17, T[15])
    b = FF(b, c, d, a, words[i + 15], 22, T[16])

    // Round 2
    a = GG(a, b, c, d, words[i + 1], 5, T[17])
    d = GG(d, a, b, c, words[i + 6], 9, T[18])
    c = GG(c, d, a, b, words[i + 11], 14, T[19])
    b = GG(b, c, d, a, words[i + 0], 20, T[20])
    a = GG(a, b, c, d, words[i + 5], 5, T[21])
    d = GG(d, a, b, c, words[i + 10], 9, T[22])
    c = GG(c, d, a, b, words[i + 15], 14, T[23])
    b = GG(b, c, d, a, words[i + 4], 20, T[24])
    a = GG(a, b, c, d, words[i + 9], 5, T[25])
    d = GG(d, a, b, c, words[i + 14], 9, T[26])
    c = GG(c, d, a, b, words[i + 3], 14, T[27])
    b = GG(b, c, d, a, words[i + 8], 20, T[28])
    a = GG(a, b, c, d, words[i + 13], 5, T[29])
    d = GG(d, a, b, c, words[i + 2], 9, T[30])
    c = GG(c, d, a, b, words[i + 7], 14, T[31])
    b = GG(b, c, d, a, words[i + 12], 20, T[32])

    // Round 3
    a = HH(a, b, c, d, words[i + 5], 4, T[33])
    d = HH(d, a, b, c, words[i + 8], 11, T[34])
    c = HH(c, d, a, b, words[i + 11], 16, T[35])
    b = HH(b, c, d, a, words[i + 14], 23, T[36])
    a = HH(a, b, c, d, words[i + 1], 4, T[37])
    d = HH(d, a, b, c, words[i + 4], 11, T[38])
    c = HH(c, d, a, b, words[i + 7], 16, T[39])
    b = HH(b, c, d, a, words[i + 10], 23, T[40])
    a = HH(a, b, c, d, words[i + 13], 4, T[41])
    d = HH(d, a, b, c, words[i + 0], 11, T[42])
    c = HH(c, d, a, b, words[i + 3], 16, T[43])
    b = HH(b, c, d, a, words[i + 6], 23, T[44])
    a = HH(a, b, c, d, words[i + 9], 4, T[45])
    d = HH(d, a, b, c, words[i + 12], 11, T[46])
    c = HH(c, d, a, b, words[i + 15], 16, T[47])
    b = HH(b, c, d, a, words[i + 2], 23, T[48])

    // Round 4
    a = II(a, b, c, d, words[i + 0], 6, T[49])
    d = II(d, a, b, c, words[i + 7], 10, T[50])
    c = II(c, d, a, b, words[i + 14], 15, T[51])
    b = II(b, c, d, a, words[i + 5], 21, T[52])
    a = II(a, b, c, d, words[i + 12], 6, T[53])
    d = II(d, a, b, c, words[i + 3], 10, T[54])
    c = II(c, d, a, b, words[i + 10], 15, T[55])
    b = II(b, c, d, a, words[i + 1], 21, T[56])
    a = II(a, b, c, d, words[i + 8], 6, T[57])
    d = II(d, a, b, c, words[i + 15], 10, T[58])
    c = II(c, d, a, b, words[i + 6], 15, T[59])
    b = II(b, c, d, a, words[i + 13], 21, T[60])
    a = II(a, b, c, d, words[i + 4], 6, T[61])
    d = II(d, a, b, c, words[i + 11], 10, T[62])
    c = II(c, d, a, b, words[i + 2], 15, T[63])
    b = II(b, c, d, a, words[i + 9], 21, T[64])

    a = (a + olda) >>> 0
    b = (b + oldb) >>> 0
    c = (c + oldc) >>> 0
    d = (d + oldd) >>> 0
  }

  // produce result as little-endian bytes
  const out = new Uint8Array(16)
  const dv = new DataView(out.buffer)
  dv.setUint32(0, a, true)
  dv.setUint32(4, b, true)
  dv.setUint32(8, c, true)
  dv.setUint32(12, d, true)
  return toHexLe(out)
}

function md5(str) {
  // 将字符串转为 UTF-8 bytes
  const encoder = new TextEncoder()
  const bytes = encoder.encode(str)
  return md5Bytes(bytes)
}

const computeHash = () => {
  errorMessage.value = ''
  if (!inputText.value) {
    outputText.value = ''
    return
  }

  try {
    let h = md5(inputText.value)
    if (uppercase.value) h = h.toUpperCase()
    outputText.value = h
  } catch (err) {
    errorMessage.value = '计算失败：' + err.message
    outputText.value = ''
  }
}

const copyToClipboard = async () => {
  if (!outputText.value) return
  try {
    const ok = await toolsCopy(outputText.value)
    if (ok) {
      copied.value = true
      setTimeout(() => (copied.value = false), 2000)
    } else {
    }
  } catch (err) {
  }
}

const clearAll = () => {
  inputText.value = ''
  outputText.value = ''
  errorMessage.value = ''
  fileInfo.value = { name: '', size: '', hash: '' }
}

const loadSample = () => {
  inputText.value = 'Hello, 世界! 这是 MD5 示例'
  computeHash()
}

const triggerFileInput = () => {
  if (fileInputRef.value) fileInputRef.value.click()
}

const handleFileChange = (e) => {
  const file = e.target.files && e.target.files[0]
  if (!file) return
  errorMessage.value = ''
  const reader = new FileReader()
  reader.onload = (ev) => {
    try {
      const ab = ev.target.result
      const h = md5ArrayBuffer(ab)
      const out = uppercase.value ? h.toUpperCase() : h
      fileInfo.value = { name: file.name, size: formatSize(file.size), hash: out }
      outputText.value = out
    } catch (err) {
      errorMessage.value = '文件哈希失败：' + err.message
    }
  }
  reader.onerror = () => {
    errorMessage.value = '读取文件失败'
  }
  reader.readAsArrayBuffer(file)
  e.target.value = ''
}

const formatSize = (bytes) => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

const downloadResult = () => {
  if (!outputText.value) return
  downloadFile(outputText.value, 'md5.txt', 'text/plain')
}

// 监听变更：输入自动计算
watch(inputText, () => {
  computeHash()
})

useHead({
  title: 'MD5 哈希 - 八八在线工具',
  meta: [{ name: 'description', content: '在线 MD5 计算工具，支持对文本或文件计算 MD5 值并复制/下载结果' }]
})
</script>
